package is.glif.fenius.translators.glambda;

import net.glambda.schemas.gnswsi3._2008._12.rdl.ResourceRequirements_Type;
import net.glambda.schemas.gnswsi3._2008._12.rdl.Timeframe_Type;
import net.glambda.schemas.gnswsi3._2008._12.rdl.ReservationStatus_Type;
import net.glambda.schemas.gnswsi3._2008._12.rdl.ReservationStatusHolder_Type;
import net.glambda.schemas.gnswsi3._2008._12.rdl.ReservationResources_Type;
import net.glambda.schemas.gnswsi3._2008._12.ndl.NetworkResources_Type;

import net.glambda.schemas.gnswsi3._2008._12.reservationfactory.ReservationFactoryPortType;
import net.glambda.schemas.gnswsi3._2008._12.reservationfactory.service.ReservationFactoryServiceAddressingLocator;
import net.glambda.schemas.gnswsi3._2008._12.reservation.types.GetReservationStatusRequest;
import net.glambda.schemas.gnswsi3._2008._12.reservation.types.GetReservationStatusResponse;
import net.glambda.schemas.gnswsi3._2008._12.reservation.ReservationPortType;
import net.glambda.schemas.gnswsi3._2008._12.reservation.service.ReservationServiceAddressingLocator;
import net.glambda.schemas.gnswsi3._2008._12.reservation.types.ModifyAllRequest;
import net.glambda.schemas.gnswsi3._2008._12.reservation.types.ModifyAllResponse;
import net.glambda.schemas.gnswsi3._2008._12.reservation.types.ModifyRequest;
import net.glambda.schemas.gnswsi3._2008._12.reservation.types.ModifyResponse;
import net.glambda.schemas.gnswsi3._2008._12.reservation.types.ReleaseAllRequest;
import net.glambda.schemas.gnswsi3._2008._12.reservation.types.ReleaseAllResponse;
import net.glambda.schemas.gnswsi3._2008._12.reservation.types.ReleaseRequest;
import net.glambda.schemas.gnswsi3._2008._12.reservation.types.ReleaseResponse;
import net.glambda.schemas.gnswsi3._2008._12.reservation.types.ReserveRequest;
import net.glambda.schemas.gnswsi3._2008._12.reservation.types.ReserveResponse;
import net.glambda.schemas.gnswsi3._2008._12.reservationfactory.types.CreateRequest;
import net.glambda.schemas.gnswsi3._2008._12.reservationfactory.types.CreateResponse;

import net.glambda.schemas.gnswsi3._2008._12.rdl.CommandStatus_Type;
import net.glambda.schemas.gnswsi3._2008._12.reservationcommand.ReservationCommandPortType;
import net.glambda.schemas.gnswsi3._2008._12.reservationcommand.service.ReservationCommandServiceAddressingLocator;
import net.glambda.schemas.gnswsi3._2008._12.reservationcommand.types.CommitRequest;
import net.glambda.schemas.gnswsi3._2008._12.reservationcommand.types.CommitResponse;
import net.glambda.schemas.gnswsi3._2008._12.reservationcommand.types.AbortRequest;
import net.glambda.schemas.gnswsi3._2008._12.reservationcommand.types.AbortResponse;
import net.glambda.schemas.gnswsi3._2008._12.reservationcommand.types.GetCommandStatusRequest;
import net.glambda.schemas.gnswsi3._2008._12.reservationcommand.types.GetCommandStatusResponse;
import org.oasis.wsrf.properties.GetResourcePropertyResponse;

import org.globus.wsrf.client.BaseClient;
import org.oasis.wsrf.faults.BaseFaultType;
import org.oasis.wsrf.lifetime.Destroy;
import org.oasis.wsrf.lifetime.DestroyResponse;
import org.oasis.wsrf.properties.InvalidResourcePropertyQNameFaultType;
import org.oasis.wsrf.resource.ResourceUnavailableFaultType;
import org.oasis.wsrf.resource.ResourceUnknownFaultType;
import org.ietf.jgss.GSSCredential;

import org.globus.axis.message.addressing.EndpointReferenceType;
import org.globus.axis.message.addressing.Address;
import org.apache.axis.types.URI.MalformedURIException;
import org.apache.axis.message.MessageElement;
import org.globus.wsrf.impl.security.descriptor.ClientSecurityDescriptor;
import org.globus.wsrf.impl.security.authorization.HostAuthorization;
import org.globus.wsrf.security.descriptor.client.ClientSecurityConfig;
import org.globus.wsrf.security.descriptor.client.GSISecureConversation;
import org.globus.wsrf.security.descriptor.client.ValStringType;
import org.globus.wsrf.security.descriptor.client.Integrity;
import org.globus.axis.gsi.GSIConstants;
import org.globus.wsrf.security.Constants;
import org.globus.wsrf.impl.security.authorization.NoAuthorization;
import javax.xml.rpc.Stub;
import javax.xml.rpc.ServiceException;
import java.rmi.RemoteException;
import javax.xml.namespace.QName;

public class ResourceManagerClient
{
    /* 60 sec wait for command status change */
    private static final int count = 60; 

    String factoryURI = null;
    GSSCredential credential = null;
    String DN = null;
    EndpointReferenceType endpoint = null;
    EndpointReferenceType cmd = null;

    public ResourceManagerClient
	(String factoryURI, String DN, GSSCredential credential)
        throws MalformedURIException, BaseFaultType, ServiceException, RemoteException
    {
        super();
	this.factoryURI = factoryURI;
	this.DN = DN;
	this.credential = credential;
	op_create();
    }

    private void secondTimer(int t) 
    {
	try {
	    Thread.sleep(t*1000);
	} catch (Exception ex) {}
    }

    private void waitCommandStatus
	(CommandStatus_Type current, CommandStatus_Type wait)
        throws BaseFaultType, ServiceException, RemoteException
    {
	int w = count;
	while (true) {
	    CommandStatus_Type c = op_getCommandStatus();
	    System.err.println("CommandStatus=" + c + ", waiting for " + wait);

	    if (c == null) {
		throw new RemoteException
		    ("CommandStatus Error::CommandStatus is null.");
	    } else if (c == current) {
		secondTimer(1);
		w --;
	    } else if (c == wait) {
		break;
	    } else {
		throw new RemoteException
		    ("CommandStatus Error::CommandStatus is " + c + 
		     ", not " + wait + ".");
	    }
	    
	    if (w == 0) {
		throw new RemoteException
		    ("CommandStatus Timeout::CommandStatus is " + c + 
		     ", not " + wait + ".");
	    }
	}
    }

    public void reserve(ResourceRequirements_Type req) 
        throws BaseFaultType, ServiceException, RemoteException
    {
	if (cmd != null) {
	    System.err.println
		("Command sequence Error, next operation is commit/abort.");
	    return;
	}

	op_reserve(req);
	waitCommandStatus
	    (CommandStatus_Type.Initial, CommandStatus_Type.Prepared);
	op_commit();
	waitCommandStatus
	    (CommandStatus_Type.Prepared, CommandStatus_Type.Committed);
	op_destroy();
    }

    public void modify(ResourceRequirements_Type req)
        throws BaseFaultType, ServiceException, RemoteException
    {
	if (cmd != null) {
	    System.err.println
		("Command sequence Error, next operation is commit/abort.");
	    return;
	}

	op_modify(req);
	waitCommandStatus
	    (CommandStatus_Type.Initial, CommandStatus_Type.Prepared);
	op_commit();
	waitCommandStatus
	    (CommandStatus_Type.Prepared, CommandStatus_Type.Committed);
	op_destroy();
    }

    public void modifyAll(Timeframe_Type req)
        throws BaseFaultType, ServiceException, RemoteException
    {
	if (cmd != null) {
	    System.err.println
		("Command sequence Error, next operation is commit/abort.");
	    return;
	}

	op_modifyAll(req);
	waitCommandStatus
	    (CommandStatus_Type.Initial, CommandStatus_Type.Prepared);
	op_commit();
	waitCommandStatus
	    (CommandStatus_Type.Prepared, CommandStatus_Type.Committed);
	op_destroy();
    }

    public void release(String[] subkey)
        throws BaseFaultType, ServiceException, RemoteException
    {
	if (cmd != null) {
	    System.err.println
		("Command sequence Error, next operation is commit/abort.");
	    return;
	}

	op_release(subkey);
	waitCommandStatus
	    (CommandStatus_Type.Initial, CommandStatus_Type.Prepared);
	op_commit();
	waitCommandStatus
	    (CommandStatus_Type.Prepared, CommandStatus_Type.Committed);
	op_destroy();
    }

    public void releaseAll()
        throws BaseFaultType, ServiceException, RemoteException
    {
	if (cmd != null) {
	    System.err.println
		("Command sequence Error, next operation is commit/abort.");
	    return;
	}

	op_releaseAll();
	waitCommandStatus
	    (CommandStatus_Type.Initial, CommandStatus_Type.Prepared);
	op_commit();
	waitCommandStatus
	    (CommandStatus_Type.Prepared, CommandStatus_Type.Committed);
	op_destroy();
    }

    private void setSecurityOption(Stub stub) 
    {
	if (true) { /* not GSI */
	    stub._setProperty(Constants.USERNAME, "fenius-translator");
	    stub._setProperty(Constants.PASSWORD, "fenius-password");
	    /*
	    stub._setProperty
		(Constants.GSI_SEC_CONV, Constants.NONE);
	    stub._setProperty
		(Constants.AUTHORIZATION, NoAuthorization.getInstance());
	    */
	    return;
	}
	
	ClientSecurityDescriptor desc = new ClientSecurityDescriptor();
	ClientSecurityConfig config = new ClientSecurityConfig();
	GSISecureConversation conv = new GSISecureConversation();
	ValStringType delegation = new ValStringType
	    (GSIConstants.GSI_MODE_FULL_DELEG);
	    
	if (credential != null) {
	    desc.setGSSCredential(credential);
	}

	conv.setDelegation(delegation);
	conv.setIntegrity(new Integrity());
	config.setGSISecureConversation(conv);
	desc.setSecurityDescriptor(config);
	desc.setAuthz(HostAuthorization.getInstance());
	stub._setProperty(Constants.CLIENT_DESCRIPTOR, desc);
    }

    public void op_create()
        throws MalformedURIException, BaseFaultType, ServiceException, 
	       RemoteException
    {
	// System.err.println("URL=" + factoryURI);
	endpoint = new EndpointReferenceType();
	endpoint.setAddress(new Address(factoryURI));

	ReservationFactoryServiceAddressingLocator locator =
	    new ReservationFactoryServiceAddressingLocator();
        ReservationFactoryPortType factory =
	    locator.getReservationFactoryPortTypePort(endpoint);
	setSecurityOption((Stub) factory);

	CreateRequest request = new CreateRequest(DN);
	CreateResponse response = factory.create(request);
	EndpointReferenceType epr = response.getReservationEPR();
	endpoint = epr;
    }
    
    public EndpointReferenceType op_reserve(ResourceRequirements_Type req) 
        throws BaseFaultType, ServiceException, RemoteException
    {
	if (cmd != null) {
	    System.err.println
		("Command sequence Error, next operation is commit/abort.");
	    return null;
	}

	ReservationServiceAddressingLocator locator =
	    new ReservationServiceAddressingLocator();
        ReservationPortType port =
            locator.getReservationPortTypePort(endpoint);
	setSecurityOption((Stub) port);

	ReserveRequest request = new ReserveRequest(req);
	ReserveResponse response = port.reserve(request);
	cmd = response.getReservationCommandEPR();
	return cmd;
    }

    public EndpointReferenceType op_modify(ResourceRequirements_Type req)
        throws BaseFaultType, ServiceException, RemoteException
    {
	if (cmd != null) {
	    System.err.println
		("Command sequence Error, next operation is commit/abort.");
	    return null;
	}

	ReservationServiceAddressingLocator locator =
	    new ReservationServiceAddressingLocator();
        ReservationPortType port =
            locator.getReservationPortTypePort(endpoint);
	setSecurityOption((Stub) port);

	ModifyRequest request = new ModifyRequest(req);
        ModifyResponse response = port.modify(request);
        cmd = response.getReservationCommandEPR();
	return cmd;
    }

    public EndpointReferenceType op_modifyAll(Timeframe_Type req)
        throws BaseFaultType, ServiceException, RemoteException
    {
	if (cmd != null) {
	    System.err.println
		("Command sequence Error, next operation is commit/abort.");
	    return null;
	}

	ReservationServiceAddressingLocator locator =
	    new ReservationServiceAddressingLocator();
        ReservationPortType port =
            locator.getReservationPortTypePort(endpoint);
	setSecurityOption((Stub) port);

	ModifyAllRequest request = new ModifyAllRequest(req);
        ModifyAllResponse response = port.modifyAll(request);
        cmd = response.getReservationCommandEPR();
	return cmd;
    }

    public EndpointReferenceType op_release(String[] subkey)
        throws BaseFaultType, ServiceException, RemoteException
    {
	if (cmd != null) {
	    System.err.println
		("Command sequence Error, next operation is commit/abort.");
	    return null;
	}
	
	ReservationServiceAddressingLocator locator =
	    new ReservationServiceAddressingLocator();
        ReservationPortType port =
            locator.getReservationPortTypePort(endpoint);
	setSecurityOption((Stub) port);

	ReleaseRequest request = new ReleaseRequest(subkey);
        ReleaseResponse response = port.release(request);
        cmd = response.getReservationCommandEPR();
	return cmd;
    }

    public EndpointReferenceType op_releaseAll()
        throws BaseFaultType, ServiceException, RemoteException
    {
	if (cmd != null) {
	    System.err.println
		("Command sequence Error, next operation is commit/abort.");
	    return null;
	}
	
	ReservationServiceAddressingLocator locator =
	    new ReservationServiceAddressingLocator();
        ReservationPortType port =
            locator.getReservationPortTypePort(endpoint);
	setSecurityOption((Stub) port);

	ReleaseAllRequest request = new ReleaseAllRequest();
        ReleaseAllResponse response = port.releaseAll(request);
        cmd = response.getReservationCommandEPR();
	return cmd;
    }

    public void op_commit()
        throws BaseFaultType, ServiceException, RemoteException
    {
	if (cmd == null) {
	    System.err.println
		("Command sequence Error, next operation is reserve/release/modify.");
	    return;
	}

        ReservationCommandServiceAddressingLocator locator =
            new ReservationCommandServiceAddressingLocator();
        ReservationCommandPortType port =
            locator.getReservationCommandPortTypePort(cmd);
	setSecurityOption((Stub) port);

	CommitRequest request = new CommitRequest();
	CommitResponse response = port.commit(request);
    }

    public void op_abort()
        throws BaseFaultType, ServiceException, RemoteException
    {
	if (cmd == null) {
	    System.err.println
		("Command sequence Error, next operation is reserve/release/modify.");
	    return;
	}

        ReservationCommandServiceAddressingLocator locator =
            new ReservationCommandServiceAddressingLocator();
        ReservationCommandPortType port =
            locator.getReservationCommandPortTypePort(cmd);
	setSecurityOption((Stub) port);

	AbortRequest request = new AbortRequest();
	AbortResponse response = port.abort(request);
    }

    private void op_destroy()
        throws BaseFaultType, ServiceException, RemoteException
    {
	if (cmd == null) {
	    System.err.println
		("Command sequence Error, next operation is reserve/release/modify.");
	    return;
	}

        ReservationCommandServiceAddressingLocator locator =
            new ReservationCommandServiceAddressingLocator();
        ReservationCommandPortType port =
            locator.getReservationCommandPortTypePort(cmd);
	setSecurityOption((Stub) port);

	Destroy request = new Destroy();
	DestroyResponse response = port.destroy(request);
	cmd = null;
    }

    public ReservationResources_Type[] getReservationResource()
        throws BaseFaultType, ServiceException, RemoteException
    {
	QName qname = new QName("http://schemas.glambda.net/gnswsi3/2008/12/rdl",
				"ReservationResources");

	ReservationServiceAddressingLocator locator =
	    new ReservationServiceAddressingLocator();
        ReservationPortType port =
            locator.getReservationPortTypePort(endpoint);
	setSecurityOption((Stub) port);

        GetResourcePropertyResponse response = port.getResourceProperty(qname);
	MessageElement[] me = response.get_any();
	if (me == null) return null;

	int size = me.length;
	ReservationResources_Type[] rrt = new ReservationResources_Type[size];
	for (int i = 0; i < size; i++) {
	    try {
		rrt[i] = (NetworkResources_Type) 
		    me[i].getObjectValue(NetworkResources_Type.class);
	    } catch (Exception ex) {
		ex.printStackTrace();
		rrt[i] = null;
	    }
	}
	return rrt;
    }

    public ReservationStatus_Type[] getReservationStatus(String[] subkey)
        throws BaseFaultType, ServiceException, RemoteException
    {
	ReservationServiceAddressingLocator locator =
	    new ReservationServiceAddressingLocator();
        ReservationPortType port =
            locator.getReservationPortTypePort(endpoint);
	setSecurityOption((Stub) port);

	GetReservationStatusRequest request = 
	    new GetReservationStatusRequest(subkey);
        GetReservationStatusResponse response = 
	    port.getReservationStatus(request);
	
	ReservationStatusHolder_Type[] holder = response.getReservationStatusHolder();
	if (holder == null) return null;
	int n = holder.length;
	ReservationStatus_Type[] result = new ReservationStatus_Type[n];
	for (int i = 0; holder != null && i < holder.length; i++) {
	    String key = holder[i].getReservationID();
	    result[i] = holder[i].getReservationStatus();
	    
	    if (subkey[i] != null && subkey.equals(key)) {
		System.err.println("****** BAD order, check key ******");
	    }
	}
	return result;
    }

    public CommandStatus_Type op_getCommandStatus()
	throws BaseFaultType , ServiceException, RemoteException
    {
	if (cmd == null) {
	    System.err.println
	    ("Command sequence Error, Command Endpoint Reference == null.");
	    return null;
	}

        ReservationCommandServiceAddressingLocator locator =
            new ReservationCommandServiceAddressingLocator();
        ReservationCommandPortType port =
            locator.getReservationCommandPortTypePort(cmd);
	setSecurityOption((Stub) port);

	GetCommandStatusRequest request = new GetCommandStatusRequest();
	GetCommandStatusResponse response = port.getCommandStatus(request);

	if (response == null) {
	    // System.err.println("null return.");
	    return null;
	}
	CommandStatus_Type status = response.getCommandStatus();
	return status;
    }

    protected void finalize()
    {
	if (endpoint == null) return;
	try {
	    ReservationServiceAddressingLocator locator =
		new ReservationServiceAddressingLocator();
	    ReservationPortType port =
		locator.getReservationPortTypePort(endpoint);
	    setSecurityOption((Stub) port);
	    
	    Destroy request = new Destroy();
	    DestroyResponse response = port.destroy(request);
	} catch (Exception ex) {}
	// System.err.println("destroy");
	endpoint = null;
    }
}
